package cz.acies.uniql.connect;

import java.io.Serializable;
import java.sql.*;
import java.util.*;

import cz.acies.uniql.dialect.*;
import cz.acies.utils.JavaUtils;

public class SQLConnect implements Serializable
{

	// ----- Class variables --------------------------------------------------

	private static final long serialVersionUID = 5124329617064797206L;
	
	public static final int MYSQL = 0;
	public static final int MSSQL = 1;
	public static final int ORACLE = 2;
	public static final int FIREBIRD = 3;
	public static final int DBASE = 4;	
	public static final int MCKOI = 5;
	public static final int HSQL = 6;
	public static final int H2DB = 7;
	public static final int POSTGRESQL = 8;
	public static final int DB2 = 9;
	public static final int MSSQL_JSQLCONNECT = 999;
	
	// ----- Constructors -----------------------------------------------------
	
	public SQLConnect() {
	}

	public SQLConnect(Properties prop, Dialect dialect) throws ClassNotFoundException, SQLException {
		try {
			this.dialect = dialect;
			if (prop != null) {
				this.properties = prop;
				String driver = (String)this.properties.getProperty("driver");
				if (driver != null) this.dialect.setSQLConnDriver(driver); 
			}
			if (dialect == null) {
				throw new SQLException("SQLConnect: Dailect is .NULL.");
			} else {
				this.getDriver();
				this.url = dialect.generateSQLConnUrl(this);
				//this.url = dialect.generateSQLConnUrl(prop);
				//System.out.println("SQLConnect.url: " + getUrl());
			}
		} catch (ClassCastException ex) {
			throw ex;
		} catch (SQLException ex) {
			throw ex;
		}
	}

	public SQLConnect(String url, Properties prop) throws ClassNotFoundException, SQLException {
		try {
			this.url = url;
			if (prop != null) {
				this.properties = prop;
			}
			this.conn = openConnection();
		} catch (ClassCastException ex) {
			throw ex;
		} catch (SQLException ex) {
			throw ex;
		}
	}

	public SQLConnect(String url, Dialect dialect) throws ClassNotFoundException, SQLException {
		try {
			this.dialect = dialect;
			if (this.dialect == null) {
				throw new SQLException("SQLConnect: Dailect is .NULL.");
			} else {
				setDriver(dialect.getSQLConnDriver());
			}
			this.url = url;
			this.conn = openConnection(this.url);
		} catch (ClassCastException ex) {
			throw ex;
		} catch (SQLException ex) {
			throw ex;
		}
	}

	// -----  -----------------------------------------------------------------
	
	public Connection openConnection(String url) throws ClassNotFoundException, SQLException {
		try {
			Class.forName(getDriver());
			this.url = url;
			Connection conn = DriverManager.getConnection(url);
			System.out.println("Opening connection...");
			return conn;
		} catch (ClassNotFoundException ex) {
			System.err.println("Class driver not found...");
			throw ex;
		} catch (SQLException ex) {
			System.err.println("Connection is not opened: " + ex);
			throw ex;
		}
	}
	
	public Connection openConnection() throws ClassNotFoundException, SQLException {
		try {
			Class.forName(getDriver());
			Connection conn = DriverManager.getConnection(getUrl(), properties);
			return conn;
		} catch (ClassNotFoundException ex) {
			System.err.println("Class driver not found...");
			throw ex;
		} catch (SQLException ex) {
			System.err.println("Connection is not opened: " + ex);
			throw ex;
		}
	}

	public Connection getConnection() throws SQLException {
		try {
			if (conn == null) {
				return openConnection();
			} else {
				try {
					conn.createStatement();
				} catch (Exception e) {
					try {
						return openConnection();				    
					} catch (Exception ex) {	
				    	ex.printStackTrace();
				    }				
				}				
			}
		} catch (ClassNotFoundException ex) {
			ex.printStackTrace();
		} catch (SQLException ex) {
			throw ex;
		}
		return conn;
	}

	public void setConnection(Connection conn) throws SQLException {
		this.conn = conn;
		if (this.conn != null) {
			DatabaseMetaData dbmd = this.conn.getMetaData();
			String url = dbmd.getURL();
			setPrefix(url.substring(0, url.indexOf(":", 6)));
		}
	}

	public void closeConnection() {
		try {
			if (conn != null)
				conn.close();
		} catch (Exception ex) {
			System.err.println("MySQLConnect.close ERROR: " + ex);
		}
	}

	public void close() {
		try {
			if (conn != null) closeConnection();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void finalize() {
		close();
	}

	// ----- Other methods ----------------------------------------------------

	public String getUrl() throws SQLException {
		return this.url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public Properties getProperties() {
		return properties;
	}
	
	public void setProperties(Properties properties) {
		this.properties = properties;
	}
	
	public void setProperty(String key, String value) {
		properties.put(key, value);
	}

	public Object getProperty(String key) {
		return properties.get(key);
	}
	
	// ----- Getter & setter methods ------------------------------------------

	public void setDriver(String driver) {
		this.driver = driver;
	}

	public String getDriver() {
		if (driver == null) {
			driver = (String) this.getProperty("driver");
			if (driver == null) {
				if (this.getDialect() != null) {
					driver = this.getDialect().getSQLConnDriver();
				}
			}
			properties.remove("driver");
		}
		//System.out.println("SQLConnect.getDriver() = "+driver);
		return driver;
	}

	/**/

	public void setPrefix(String prefix) {
		this.prefix = prefix;
	}

	public String getPrefix() {
		if (prefix == null) {
			prefix = (String) this.getProperty("prefix");
			if (prefix == null) {
				if (this.getDialect() != null) {
					prefix = this.getDialect().getSQLConnPrefix();
				}
			}
			this.properties.remove("prefix");
		}
		return prefix;
	}

	/**/

	public void setHost(String host) {
		this.host = host;
	}

	public String getHost() {
		if (this.host == null) {
			this.host = (String) getProperty("host");
			this.properties.remove("host");
		}
		return this.host;
	}

	/**/

	public void setDatabase(String dbname) {
		this.database = dbname;
	}

	public String getDatabase() {
		if (this.database == null) {
			this.database = (String) getProperty("database");
		}
		return this.database;
	}

	/**/

	public void setPort(String port) {
		this.port = port;
	}

	public String getPort() {
		if (this.port == null) {
			this.port = (String) getProperty("port");
			properties.remove("port");
		}
		return this.port;
	}

	/**/

	public void setUserName(String user) {
		setProperty("user", user);
	}

	public String getUserName() {
		return (String) getProperty("user");
	}

	/**/

	public void setPassword(String passwd) {
		setProperty("password", passwd);
	}

	public String getPassword() {
		return (String) getProperty("password");
	}

	public void setSQLConnSchema(String schema) {
		this.schema = schema;
	}

	public String getSQLConnSchema() {
		return this.schema;
	}
		
	// -----  -----------------------------------------------------------------
	
	public void setDialect(Dialect dialect) {
		this.dialect = dialect;
	}

	public Dialect getDialect() {
		if (this.dialect == null) {
			String className = (String) getProperty("dialectClass");
			if (className != null) {
				try {
					this.dialect = (Dialect) JavaUtils.createObjectInstance(className);
				} catch (Exception ex) {
					this.dialect = gendict();
				}
			} else {
				this.dialect = gendict();
			}
		}
		return this.dialect;
	}

	private Dialect gendict() {
		//System.out.println("SQLConnect.gendict()");
		String prefix = getPrefix();
		String className = "";
		if (prefix.equals("jdbc:mysql:")) {
			className = "cz.acies.uniql.dialect.MySQLDialect";
		} else if (prefix.equals("jdbc:JSQLConnect:")) {
			className = "cz.acies.uniql.dialect.MSSQLDialect";
		} else if (prefix.equals("jdbc:sqlserver:")) {
			className = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; 
		} else if (prefix.equals("jdbc:DBF:")) {
			className = "cz.acies.uniql.dialect.dBaseDialect";
		} else if (prefix.equals("jdbc:firebirdsql:")) {
			className = "cz.acies.uniql.dialect.FirebirdDialect";
		} else if (prefix.equals("jdbc:oracle:thin:")) {
			className = "oracle.jdbc.driver.OracleDriver";			
		}
		try {
			this.dialect = (Dialect) JavaUtils.createObjectInstance(className);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return this.dialect;
	}

	
	// ----- Class variables --------------------------------------------------

	private String url = null;
	private String driver = null;

	private String prefix = null;
	private String host = null;
	private String port = null;
	private String database = null;

	private String schema = null;
	private Dialect dialect = null;
	private Connection conn = null;
	private Properties properties = new Properties();

}
